/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2019.
 * Description: support cache dfx/ecc feature
 * Author: qiuguorui <qiuxi1@huawei.com>
 * Create: 2018-09-27
 */

#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

enum rtos_notifier_type {
	RTOS_KERNEL_EARLY_DIE = 0,
	RTOS_USER_EARLY_DIE,
	RTOS_USER_DIE,
	RTOS_EARLY_RESET,
	RTOS_EARLY_PANIC,
	RTOS_MAX_TYPE,
};

#ifdef CONFIG_HISI_EARLY_DIE_NOTIFIER
static ATOMIC_NOTIFIER_HEAD(early_die_notifier_chain);
int register_early_die_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR"Register cache ecc notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_register(&early_die_notifier_chain, nb);
}
EXPORT_SYMBOL(register_early_die_notifier);

int unregister_early_die_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR"Unregister die notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_unregister(&early_die_notifier_chain, nb);
}
EXPORT_SYMBOL(unregister_early_die_notifier);

int call_early_die_notifiers(unsigned int user_mode, void *reg)
{
	unsigned long notifier_type;

	if (user_mode)
	notifier_type = RTOS_USER_EARLY_DIE;
	else
	notifier_type = RTOS_KERNEL_EARLY_DIE;

	return atomic_notifier_call_chain(&early_die_notifier_chain, notifier_type, reg);
}
#endif

#ifdef CONFIG_HISI_USR_DIE_NOTIFIER
static ATOMIC_NOTIFIER_HEAD(usr_die_notifier_chain);
int register_usr_die_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR "register usr_die notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_register(&usr_die_notifier_chain, nb);
}
EXPORT_SYMBOL_GPL(register_usr_die_notifier);

int unregister_usr_die_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR "unregister usr_die notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_unregister(&usr_die_notifier_chain, nb);
}
EXPORT_SYMBOL_GPL(unregister_usr_die_notifier);

int call_usr_die_notifiers(void)
{
	return atomic_notifier_call_chain(&usr_die_notifier_chain, RTOS_USER_DIE, NULL);
}
#endif

#ifdef CONFIG_HISI_EARLY_RESET_NOTIFIER
static ATOMIC_NOTIFIER_HEAD(early_reset_notifier_chain);
int register_early_reset_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR "register reset notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_register(&early_reset_notifier_chain, nb);
}
EXPORT_SYMBOL_GPL(register_early_reset_notifier);

int unregister_early_reset_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR "unregister reset notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_unregister(&early_reset_notifier_chain, nb);
}
EXPORT_SYMBOL_GPL(unregister_early_reset_notifier);

int call_early_reset_notifiers(void)
{
	return atomic_notifier_call_chain(&early_reset_notifier_chain, RTOS_EARLY_RESET, NULL);
}
#endif
#ifdef CONFIG_HISI_EARLY_PANIC_NOTIFIER
static ATOMIC_NOTIFIER_HEAD(early_panic_notifier_chain);
int register_early_panic_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR "register early_panic notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_register(&early_panic_notifier_chain, nb);
}
EXPORT_SYMBOL_GPL(register_early_panic_notifier);

int unregister_early_panic_notifier(struct notifier_block *nb)
{
	if (nb == NULL) {
	    printk(KERN_ERR "unregister early_panic notifier failed\n");
	    return -ENOENT;
	}

	return atomic_notifier_chain_unregister(&early_panic_notifier_chain, nb);
}
EXPORT_SYMBOL_GPL(unregister_early_panic_notifier);

int call_early_panic_notifiers(void)
{
	return atomic_notifier_call_chain(&early_panic_notifier_chain, RTOS_EARLY_PANIC, NULL);
}
#endif

#ifdef CONFIG_ARM64
int panic_on_cacheecc;
EXPORT_SYMBOL(panic_on_cacheecc);

void set_panic_on_cacheecc(void)
{
	panic_on_cacheecc = 1; /* force panic */
}
EXPORT_SYMBOL_GPL(set_panic_on_cacheecc);
#endif
